﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using NAudio.Midi;

namespace MidiPlayer
{
    public partial class MidiSettingsForm : Form
    {
        private readonly MidiSettingsViewModel Model;
        public MidiSettingsDto Settings { get { return Model.Settings;  } }

        private MidiIn midiIn;
        private MidiOut midiOut;
        bool monitoring;

        public event StopMonitoringHandler StopEvent;
        public delegate void StopMonitoringHandler(Object o, EventArgs e);

        public MidiSettingsForm(MidiSettingsDto dto) 
        {
            Model = new MidiSettingsViewModel(dto);

            InitializeComponent();

            UpdateView();

            StopEvent += new StopMonitoringHandler(OnStopEvent);
        }

        private void OnStopEvent(Object o, EventArgs e)
        {
            StopMonitoring();
        }

        private void UpdateView()
        {
            Model.Load();

            foreach(var inName in Model.AvailableMidiIns)
            {
                cbMidiIn.Items.Add(inName);
            }
            if(cbMidiIn.Items.Count > 0)
            {
                int inIndex = Model.FindDevice(Settings.MidiInName, true);
                if (inIndex == -1) inIndex = 0;
                cbMidiIn.SelectedIndex = inIndex;
            }

            foreach (var outName in Model.AvailableMidiOuts)
            {
                cbMidiOut.Items.Add(outName);
            }
            if (cbMidiOut.Items.Count > 0)
            {
                int inIndex = Model.FindDevice(Settings.MidiOutName, false);
                if (inIndex == -1) inIndex = 0;
                cbMidiOut.SelectedIndex = inIndex;
            }

            tbMidiOutChannel.Text = Settings.MidiOutChannel.ToString();
            tbNote.Text = Settings.MidiPlayNote.ToString();

            chStopOnRelease.Checked = Settings.StopOnNoteRelease;
            if(Settings.MidiMode == MidiSettingsDto.OutputMode.PlayFile)
            {
                rbPlay.Checked = true;
            }
            else if (Settings.MidiMode == MidiSettingsDto.OutputMode.PassThru)
            {
                rbPassThru.Checked = true;
            }


        }


        bool UpdateModel()
        {
            if (rbPlay.Checked)
            {
                Settings.MidiMode = MidiSettingsDto.OutputMode.PlayFile;
            }
            else if (rbPassThru.Checked)
            {
                Settings.MidiMode = MidiSettingsDto.OutputMode.PassThru;
            }
            else
            {
                MessageBox.Show("Choose valid MIDI mode");
                return false;
            }

            int chan;
            if (int.TryParse(tbMidiOutChannel.Text, out chan))
            {
                if (chan < 1  || chan > 1024)
                {
                    MessageBox.Show("Enter valid MIDI Out channel (0 to 1024)");
                    tbMidiOutChannel.Focus();
                    return false;
                }
                Settings.MidiOutChannel = chan;
            }
            else
            {
                MessageBox.Show("Enter valid MIDI Out channel");
                tbMidiOutChannel.Focus();
                return false;
            }

            Settings.StopOnNoteRelease = chStopOnRelease.Checked;
            Settings.MidiInName = cbMidiIn.SelectedIndex != -1 ?
                Model.AvailableMidiIns[cbMidiIn.SelectedIndex] : String.Empty;

            Settings.MidiOutName = cbMidiOut.SelectedIndex != -1 ?
                Model.AvailableMidiOuts[cbMidiOut.SelectedIndex] : String.Empty;

            return true;
        }

        private void btnOk_Click(object sender, EventArgs e)
        {
            if(UpdateModel())
                this.Close();
        }


        private void btnNote_Click(object sender, EventArgs e)
        {
            if (!monitoring)
            {
                if(UpdateModel())
                    StartMonitoring();
            }
            else
                StopMonitoring();


        }

        private void StopMonitoring()
        {
            if (monitoring)
            {
                if(midiIn != null)
                    midiIn.Stop();
                monitoring = false;
                cbMidiIn.Enabled = true;
                cbMidiOut.Enabled = true;
                rbPassThru.Enabled = true;
                rbPlay.Enabled = true;
                btnNote.Text = "Press note...";
                tbNote.Text = Settings.MidiPlayNote.ToString();
            }
        }

        private void StartMonitoring()
        {
            if (midiIn == null)
            {
                midiIn = new MidiIn(cbMidiIn.SelectedIndex);
                midiIn.MessageReceived += new EventHandler<MidiInMessageEventArgs>(midiIn_MessageReceived);
                midiIn.ErrorReceived += new EventHandler<MidiInMessageEventArgs>(midiIn_ErrorReceived);
            }

            midiIn.Start();
            monitoring = true;
            btnNote.Text = "Stop"; 
            cbMidiIn.Enabled = false;
            cbMidiOut.Enabled = false;
            rbPassThru.Enabled = false;
            rbPlay.Enabled = false;

            if(Settings.MidiMode == MidiSettingsDto.OutputMode.PassThru 
                && cbMidiOut.SelectedIndex != -1)
            {
                if (midiOut == null)
                {
                    midiOut = new MidiOut(cbMidiOut.SelectedIndex);
                }
            }

        }


        void midiIn_MessageReceived(object sender, MidiInMessageEventArgs e)
        {
            if (Settings.MidiMode == MidiSettingsDto.OutputMode.PlayFile)
            {
                if (e.MidiEvent.CommandCode == MidiCommandCode.NoteOn)
                {
                    var note = (NoteEvent) (e.MidiEvent);
//                    if (StopEvent != null) StopEvent(this, null);

                    Settings.MidiPlayNote = note.NoteNumber;
                    
                }
            }
            else if(Settings.MidiMode == MidiSettingsDto.OutputMode.PassThru)
            {
                if(midiOut != null &&
                    e.MidiEvent.CommandCode == MidiCommandCode.NoteOn || 
                    e.MidiEvent.CommandCode == MidiCommandCode.NoteOff|| 
                    e.MidiEvent.CommandCode == MidiCommandCode.ControlChange)
                {
                    MidiEvent SendEvent = e.MidiEvent;
                    SendEvent.Channel = Settings.MidiOutChannel;
                    midiOut.Send(SendEvent.GetAsShortMessage());
                }
            }

        }

        void midiIn_ErrorReceived(object sender, MidiInMessageEventArgs e)
        {
            /*
            progressLog1.LogMessage(Color.Red, String.Format("Time {0} Message 0x{1:X8} Event {2}",
                e.Timestamp, e.RawMessage, e.MidiEvent));
             */
        }

        private void MidiSettingsForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            StopMonitoring();
            if (midiIn != null)
            {
                midiIn.Dispose();
                midiIn = null;
            }
            if (midiOut != null)
            {
                midiOut.Dispose();
                midiOut = null;
            }
        }

    }
}
